using System;
using System.Collections.Generic;

namespace Oops.Xna.Framework.GameState
{
	/// <summary>
	/// A collection of game services for a <see cref="GameScreen"/>.
	/// </summary>
	public class GameScreenServiceContainer : IServiceProvider 
	{
		private IServiceProvider parentProvider;
		private Dictionary<Type, object> services = new Dictionary<Type, object>();

		/// <summary>
		/// Initializes a new instance of the <see cref="GameScreenServiceContainer"/> class.
		/// </summary>
		public GameScreenServiceContainer() { }

		/// <summary>
		/// Initializes a new instance of the <see cref="GameScreenServiceContainer"/> class
		/// using the specified parent service provider.
		/// </summary>
		/// <param name="parentProvider">A parent service provider.</param>
		public GameScreenServiceContainer(IServiceProvider parentProvider)
		{
			// Hold onto parameters.
			this.parentProvider = parentProvider;
		}

		/// <summary>
		/// Adds the specified service to the service container.
		/// </summary>
		/// <param name="serviceType">The type of service to add.</param>
		/// <param name="serviceInstance">An instance of the service to add. 
		/// This object must implement or inherit from the type indicated by 
		/// the <paramref name="serviceType"/> parameter.</param>
		public void AddService(Type serviceType, object serviceInstance)
		{
			// The service type parameter must be specified.
			if (serviceType == null)
			{
				throw new ArgumentNullException("serviceType");
			}

			// The service instance parameter must be specified.
			if (serviceInstance == null)
			{
				throw new ArgumentNullException("serviceInstance");
			}

			// The service type cannot already exist in the service container.
			if (this.services.ContainsKey(serviceType))
			{
				throw new GameServiceAlreadyExistsException(serviceType);
			}

			// The service instance must be of the type specified.
			if (!serviceType.IsAssignableFrom(serviceInstance.GetType()))
			{
				throw new ArgumentException("string.Format(Oops.Xna.Framework.Properties.Resources.GameServiceMustBeAssignable, serviceInstance.GetType().FullName, serviceType.GetType().FullName)");
			}

			// Add the service to the collection.
			this.services.Add(serviceType, serviceInstance);
		}

		/// <summary>
		/// Removes the specified service type from the service container.
		/// </summary>
		/// <param name="serviceType">The type of service to remove.</param>
		public void RemoveService(Type serviceType)
		{
			// The service type parameter must be specified.
			if (serviceType == null)
			{
				throw new ArgumentNullException("serviceType");
			}

			// Remove the service type from the collection.
			this.services.Remove(serviceType);
		}

		#region IServiceProvider Members

		public object GetService(Type serviceType)
		{
			// The service type parameter must be specified.
			if (serviceType == null)
			{
				throw new ArgumentNullException("serviceType");
			}

			// Return the service if it exists.
			object service;
			if (this.services.TryGetValue(serviceType, out service))
			{
				return service;
			}

			// Return the service from the parent provider if it exists.
			if (this.parentProvider != null)
			{
				return this.parentProvider.GetService(serviceType);
			}

			// The service was not found.
			return null;
		}

		#endregion
	}
}
